/**
 * class pineapple.Processor
 *
 * March 28, 2012 
 * @authors Roman Kotusev, Darjuš Ilcevič, Kristis Vaškys
 */
/*Klasė imituojanti procesoriaus darbą. Kintamieji atitinka registrus,
 * metodai skirti darbui su registrais.
 */
package pineapple;

public class Processor {
    /*Kintamieji*/

    //---Pagrindiniai registrai---
    private Integer R1;
    private Integer R2;
    //---Puslapiavimo registrai---
    private Integer PDS;
    private Integer PCS;
    private Integer PSS;
    //---Virtualios mašinos regitrai---
    private Integer DS;
    private Integer CS;
    private Integer SS;
    //---Požymių registrai----
    private Integer IF; //pertraukimo
    private Integer CF; //palyginimo
    //---Rodykle į atmintį--
    Memory memory;
    //---Rodyklė į kanalų įrenginį---
    ChannelDevice channel;

    /*Konstruktoriai*/
    public Processor(Memory m, ChannelDevice c) {
        memory = m;
        R1 = 0;
        R2 = 0;
        PDS = 0;
        PCS = 0;
        PSS = 0;
        DS = 0;
        CS = 0;
        SS = 0;
        CF = 0;
        IF = 0;
        channel = c;
    }

    public Processor() {
    }
    /*Methods*/
    /*Registrų, atminties išvalymas*/
    public void resetRegistres() {
        R1 = 0;
        R2 = 0;
        DS = 0;
        CS = 0;
        SS = 0;
        CF = 0;
        IF = 0;
    }

    public void resetMemory() {
        int i;
        for (i = 0; i < 64; i++) {
            memory.setNumber(null, i + PCS);
        }
        for (i = 0; i < 64; i++) {
            memory.setNumber(null, i + PDS);
        }
        for (i = 0; i < 64; i++) {
            memory.setNumber(null, i + PSS);
        }
    }

    /*Set-Get*/
    void setR1(int newR1) {
        R1 = newR1;
    }

    int getR1() {
        return R1;
    }

    void setR2(int newR2) {
        R2 = newR2;
    }

    int getR2() {
        return R2;
    }

    void setPDS(int newPDS) {
        PDS = newPDS;
    }

    int getPDS() {
        return PDS;
    }

    void setPCS(int newPCS) {
        PCS = newPCS;
    }

    int getPCS() {
        return PCS;
    }

    void setPSS(int newPSS) {
        PSS = newPSS;
    }

    int getPSS() {
        return PSS;
    }

    void setDS(int newDS) {
        DS = newDS;
    }

    int getDS() {
        return DS;
    }

    void setCS(int newCS) {
        CS = newCS;
    }

    int getCS() {
        return CS;
    }

    void setSS(int newSS) {
        SS = newSS;
    }

    int getSS() {
        return SS;
    }

    void setIF(int newIF) {
        IF = newIF;
    }

    int getIF() {
        return IF;
    }

    void setCF(int newCF) {
        CF = newCF;
    }

    int getCF() {
        return CF;
    }

    void setCodeCommand(int address, String command) {
        memory.setWord(command, PCS + address);
    }

    String getNextCommand() {
        return memory.getWord(PCS + CS);
    }
    /*Petraukimų mechanizmas*/
    public void interrupt() {
        CS++;
        switch (IF) {
            case 1: //Klaidos pranesimas
                channel.errorOutput("Klaida! Dalyba is nulio");
                break;
            case 2: //Neteisinga komanda
                channel.errorOutput("Komandos klaida!");
                break;
            case 3: //Neteisinga komanda
                channel.errorOutput("Ivedimo klaida!");
                break;
            case 11://Skaiciaus ivestis is klaviaturos "INT B"
                try {
                    Integer number;
                    number = channel.numberInput();
                    memory.setNumber(number, PDS + DS);
                    IF = 0;
                } catch (Exception e) {
                    IF = 3;
                    interrupt();
                }
                break;
            case 12://zodio ivedimas is klaviataturos "INT C"
                try {
                    String message;
                    message = channel.wordInput();
                    if (message.length() > 4) {
                        message = message.substring(0, 4);
                    }
                    memory.setWord(message, PDS + DS);
                    IF = 0;
                } catch (Exception e) {
                    IF = 3;
                    interrupt();
                }
                break;
            case 13: //Skaiciaus isvedimas i ekrana. "INT D"
                channel.numberOutput(memory.getNumber(DS + PDS));
                IF = 0;
                DS++;
                break;
            case 14: //zodzio isvedimas i ekrana "INT E"
                String msg = "";
                while (memory.getWord(DS + PDS).indexOf("$") == -1) {
                    msg = msg + memory.getWord(DS + PDS);
                    DS++;
                }
                ;
                msg = msg + memory.getWord(DS + PDS).substring(0, memory.getWord(DS + PDS).indexOf("$"));
                channel.wordOutput(msg);
                IF = 0;
                DS++;

                break;
            case 15: //Darbo pabaiga "INT F"
                channel.wordOutput(": Programos darbo pabaiga");
                break;
            default:

        }


    }
    /*Aritmetines komandos*/
    public void ADD() {
        R1 += R2;
        IF = 0;
        interrupt();
    }

    public void SUB() {
        R1 -= R2;
        IF = 0;
        interrupt();
    }

    public void MUL() {
        R1 = R1 * R2;
        IF = 0;
        interrupt();
    }

    public void DIV() {
        if (R2 == 0) {
            IF = 1;
            interrupt();
        } else {
            R1 = R1 / R2;
            IF = 0;
            interrupt();
        }
    }

    public void MOD() {
        if (R1 == 0) {
            IF = 1;
            interrupt();
        } else {
            R1 = R1 % R2;
            IF = 0;
            interrupt();
        }
    }

    public void CMP() {
        if (R1 == R2) {
            CF = 0;
            IF = 0;
            interrupt();
        }
        if (R1 > R2) {
            CF = 1;
            IF = 0;
            interrupt();
        }
        if (R1 < R2) {
            CF = 2;
            IF = 0;
            interrupt();
        }
    }
    /*Valdymo komandos*/

    public void JM(Integer pointer) {
        CS = pointer;
    }

    public void JE(Integer pointer) {
        if (CF == 0) {
            CS = pointer;
        } else {
            interrupt();
        }
    }

    public void JN(Integer pointer) {
        if (CF != 0) {
            CS = pointer;
        } else {
            interrupt();
        }
    }

    public void JA(Integer pointer) {
        if (CF == 1) {
            CS = pointer;
        } else {
            interrupt();
        }
    }

    public void JB(Integer pointer) {
        if (CF == 2) {
            CS = pointer;
        } else {
            interrupt();
        }
    }
    public void IN(String registre) {
        if (registre.equals("R1")) {
            R1++;
        } else if (registre.equals("R2")) {
            R2++;
        } else if (registre.equals("DS")) {
            DS++;
        } else if (registre.equals("CS")) {
            CS++;
        } else if (registre.equals("SS")) {
            SS++;
        } else {
            IF = 2;
        }
        interrupt();
    }

    public void DE(String registre) {
        if (registre.equals("R1")) {
            R1--;
        } else if (registre.equals("R2")) {
            R2--;
        } else if (registre.equals("DS")) {
            DS--;
        } else if (registre.equals("CS")) {
            CS--;
        } else if (registre.equals("SS")) {
            SS--;
        } else {
            IF = 2;
        }
        interrupt();
    }

    public void INT(Integer code) {
        IF = code;
        interrupt();
    }

    public void STOP() {
        IF = 15;
        interrupt();
    }
    //Darbo su duomenimis komandos

    public void L1(Integer pointer) {
        if (memory.getNumber(pointer + PDS) != null) {
            R1 = memory.getNumber(pointer + PDS);
            IF = 0;
            interrupt();
        } else {
            IF = 2;
            interrupt();
        }
    }

    public void L2(Integer pointer) {
        if (memory.getNumber(pointer + PDS) != null) {
            R2 = memory.getNumber(pointer + PDS);
            IF = 0;
            interrupt();
        } else {
            IF = 2;
            interrupt();
        }
    }

    public void R1(Integer pointer) {
        memory.setNumber(R1, pointer + PDS);
        DS++;
        IF = 0;
        interrupt();
    }

    public void R2(Integer pointer) {
        memory.setNumber(R2, pointer + PDS);
        DS++;
        IF = 0;
        interrupt();
    }

    public void DJ(Integer pointer) {
        DS = pointer;
        interrupt();
    }

    public void SWCH() {
        memory.setNumber(R1, SS + PSS);
        R1 = R2;
        R2 = memory.getNumber(SS + PSS);
        IF = 0;
        memory.setNumber(null, SS + PSS);
        interrupt();
    }
    //Steko operacijos

    public void PUSH() {
        memory.setNumber(R1, SS + PSS);
        SS++;
        IF = 0;
        if (SS == 64) {
            IF = 2;
        }
        interrupt();
    }

    public void POP() {
        SS--;
        R1 = memory.getNumber(SS + PSS);
        memory.setNumber(null, SS + PSS);
        IF = 0;
        if (R1 == null) {
            IF = 2;
        }
        if (SS == -1) {
            IF = 2;
        }
        interrupt();
    }
    //Duomenu pakrovimo komandos

    public void LW(String word, Integer pointer) {
        memory.setWord(word, pointer + PDS);
    }

    public void LN(Integer n, Integer pointer) {
        memory.setNumber(n, pointer + PDS);
    }
    //Testavimui
    public void printRegistres() {
        msg("R1: " + R1);
        msg("R2: " + R2);
        msg("CS: " + CS);
        msg("DS: " + DS);
        msg("SS: " + SS);
        msg("PCS: " + PCS);
        msg("PDS: " + PDS);
        msg("PSS: " + PSS);
        msg("CF: " + CF);
        msg("IF: " + IF);
    }

    // Isvedimui
    void msg(String mes) {
        System.out.println(mes);
    }
}
